/*
 * Copyright (c) [2022] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "cg_pgo_gen.h"
#include "optimize_common.h"
#include "instrument.h"
#include "cg.h"
namespace maplebe {
uint64 CGProfGen::counterIdx = 0;
void CGProfGen::InstrumentFunction() {
  instrumenter.PrepareInstrumentInfo(f->GetFirstBB(), f->GetCommonExitBB());
  std::vector<maplebe::BB*> iBBs;
  instrumenter.GetInstrumentBBs(iBBs, f->GetFirstBB());
  /* skip large bb function currently due to offset in ldr/store */
  if (iBBs.size() > kMaxPimm8) {
    return;
  }

  uint32 oldTypeTableSize = GlobalTables::GetTypeTable().GetTypeTableSize();
  MIRSymbol *bbCounterTab = GetOrCreateProfSymForFunc(f->GetFunction(), static_cast<uint32>(iBBs.size() + 1U));
  BECommon *be = Globals::GetInstance()->GetBECommon();
  uint32 newTypeTableSize = GlobalTables::GetTypeTable().GetTypeTableSize();
  if (newTypeTableSize != oldTypeTableSize) {
    ASSERT(be, "get be in CGProfGen::InstrumentFunction() failed");
    be->AddNewTypeAfterBecommon(oldTypeTableSize, newTypeTableSize);
  }
  for (uint32 i = 0; i < iBBs.size(); ++i) {
    InstrumentBB(*iBBs[i], *bbCounterTab, i);
  }
}

void CGProfGen::CreateProfileCalls() {
  static bool created = false;
  if (created) {
    return;
  }
  created = true;

  // Create symbol for recording call times
  auto *mirBuilder = f->GetFunction().GetModule()->GetMIRBuilder();
  auto *u64Type = GlobalTables::GetTypeTable().GetUInt64();
  std::string symNameCallTimes = "MCC_profile_call_times";
  auto *symCallTimes = mirBuilder->GetOrCreateGlobalDecl(symNameCallTimes, *u64Type);
  MIRIntConst *constVal = GlobalTables::GetIntConstTable().GetOrCreateIntConst(0, *u64Type);
  symCallTimes->SetKonst(constVal);
  symCallTimes->SetStorageClass(kScGlobal);

  /* Insert ClearProfile */
  CreateClearIcall(*f->GetFirstBB(), "MCC_ClearProfile");

  /* Insert SaveProfile */
  for (auto *bb : f->GetCommonExitBB()->GetPreds()) {
    CreateIcallForWeakSymbol(*bb, "MCC_SaveProfile");
  }
}

bool CgPgoGen::PhaseRun(maplebe::CGFunc &f) {
  if (!LiteProfile::IsInWhiteList(f.GetName()) && f.GetName() != CGOptions::GetLitePgoOutputFunction()) {
    return false;
  }
  CHECK_FATAL(f.NumBBs() < LiteProfile::GetBBNoThreshold(), "stop ! bb out of range!");

  CGProfGen *cgProfGeg = f.GetCG()->CreateCGProfGen(*GetPhaseMemPool(), f);
  cgProfGeg->InstrumentFunction();
  if (f.GetName() == CGOptions::GetLitePgoOutputFunction()) {
    cgProfGeg->CreateProfileCalls();
  }
  return false;
}
MAPLE_TRANSFORM_PHASE_REGISTER(CgPgoGen, cgpgogen)
}
